﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QLabValues;
using ParserCore;
using AtomicTypes;

namespace NumericalCalculations
{
    public partial class Functions
    {
        //fja ispituje jednakost nizova (NaN != NaN)
        public List<IQLabValue> isequal(List<IQLabValue> o)
        {
            //fja prima 2 ili vise argumenta
            if(o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> isequal \n Not enough input arguments."));

            if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
            {
                //konvertovanje u float64
                List<QFloat64NDimArray> of = new List<QFloat64NDimArray>();
                foreach (var oel in o)
                    of.Add(new QFloat64NDimArray(oel));

                List<IQLabValue> res = new List<IQLabValue>();
                AtomicTypes.QBoolean[] val = new AtomicTypes.QBoolean[1];

                //da li su nizovi (matrice) istih dimenzija
                for(int i = 1; i < of.Count; i++)
                    if (!Helper.checkDimensions(of.ElementAt(0), of.ElementAt(i)))
                    {
                        val[0] = new QBoolean(false);
                        res.Add(new QBooleanNDimArray(val, 1, 1));
                        return res;
                    }
                
                //provera jednakosti elemenata
                int n = of.ElementAt(0).Data.Length;
                for(int i = 1; i < of.Count; i++)
                    for (int j = 0; j < n; j++)
                        if ((of.ElementAt(0).Data[j].Real != of.ElementAt(i).Data[j].Real) || (of.ElementAt(0).Data[j].Imag != of.ElementAt(i).Data[j].Imag))
                        {
                            val[0] = new QBoolean(false);
                            res.Add(new QBooleanNDimArray(val, 1, 1));
                            return res;
                        }

                //inace nizovi su jednaki
                val[0] = new QBoolean(true);
                res.Add(new QBooleanNDimArray(val, 1, 1));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'isequal' for input arguments of type " +
                    Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
        }

        //fja ispituje jednakost nizova (NaN == NaN)
        public List<IQLabValue> isequalwithequalnans(List<IQLabValue> o)
        {
            //fja prima 2 ili vise argumenta
            if (o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> isequalwithequalnans \n Not enough input arguments."));

            if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
            {
                //konvertovanje u float64
                List<QFloat64NDimArray> of = new List<QFloat64NDimArray>();
                foreach (var oel in o)
                    of.Add(new QFloat64NDimArray(oel));

                List<IQLabValue> res = new List<IQLabValue>();
                AtomicTypes.QBoolean[] val = new AtomicTypes.QBoolean[1];

                // da li su nizovi (matrice) istih dimenzija
                for (int i = 1; i < of.Count; i++)
                    if (!Helper.checkDimensions(of.ElementAt(0), of.ElementAt(i)))
                    {
                        val[0] = new QBoolean(false);
                        res.Add(new QBooleanNDimArray(val, 1, 1));
                        return res;
                    }
                
                //provera jednakosti elemenata
                //radi i za prazne nizove!
                int n = of.ElementAt(0).Data.Length;
                for (int i = 1; i < of.Count; i++)
                    for (int j = 0; j < n; j++)
                    {
                        if (((of.ElementAt(0).Data[j].Real == of.ElementAt(i).Data[j].Real) ||
                            (double.IsNaN(of.ElementAt(0).Data[j].Real) && double.IsNaN(of.ElementAt(i).Data[j].Real))) // jendaki realni delovi
                            &&
                            ((of.ElementAt(0).Data[j].Imag == of.ElementAt(i).Data[j].Imag) ||
                            (double.IsNaN(of.ElementAt(0).Data[j].Imag) && double.IsNaN(of.ElementAt(i).Data[j].Imag)))) // jednaki imaginarni delovi
                            continue;
                        else
                        {
                            val[0] = new QBoolean(false);
                            res.Add(new QBooleanNDimArray(val, 1, 1));
                            return res;
                        }
                    }

                //inace nizovi su jednaki
                val[0] = new QBoolean(true);
                res.Add(new QBooleanNDimArray(val, 1, 1));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'isequalwithequalnans' for input arguments of type " +
                    Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
        }

        /*proverava da li je niz (neopadajuce) sortiran
         *ako je prvi argument 2-D matrica onda mora biti prisutan i drugi argument koji ima vredost 'rows'
         *ne radi za matrice vecih dimenzija (tj. multidimenzione nizove)!
         */
        public List<IQLabValue> issorted(List<IQLabValue> o)
        {
            //fja prima 1 ili 2 argumenta, u suprotnom - exception
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> issorted \n Too many input arguments."));
            else if (o.Count < 1)
                throw (new NumericalCalculationsException("??? Error using ==> issorted \n Not enough input arguments."));

            List<IQLabValue> res = new List<IQLabValue>();
            QBoolean[] val = new QBoolean[1];

            if(Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                //konvertuje se samo prvi argument (cija se sortiranost ispituje) jer je drugi argument string ('rows')
                QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));

                if (o.Count == 1)
                {
                    //ako je prosledjen prazan niz bilo koje dimenzije (npr. 1x0x3) vraca se true
                    //dakle, za prazan niz ne vazi da mora biti vektor ili 2-D matrica...
                    if (first.Data.Length == 0)
                    {
                        val[0] = new QBoolean(true);
                        res.Add(new QBooleanNDimArray(val, 1, 1));
                        return res;
                    }

                    //ako je prosledjen samo jedan argument on mora biti vektor
                    if (first.Dimension > 2 || first.DimensionSize[0] > 1)
                        throw (new NumericalCalculationsException("??? Error using ==> issorted \n Input must be a vector or 'rows' must be specified."));

                    //provera da li je vektor rastuce sortiran (tako radi Matlab)
                    for (int i = 0; i < first.Data.Length - 1; i++)
                        if (!Helper.lessThanOrEqualComplex(first.Data[i].Real, first.Data[i].Imag, first.Data[i + 1].Real, first.Data[i + 1].Imag))
                        {
                            val[0] = new QBoolean(false);
                            res.Add(new QBooleanNDimArray(val, 1, 1));
                            return res;
                        }

                    //inace vektor je sortiran
                    val[0] = new QBoolean(true);
                    res.Add(new QBooleanNDimArray(val, 1, 1));
                    return res;
                }
                //inace prosledjena su dva argumenta
                else
                {
                    if(Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QCharNDimArray))
                    {
                        QCharNDimArray second = new QCharNDimArray(o.ElementAt(1));

                        //ako second nije string
                        if(!Helper.IsString(second))
                            throw (new NumericalCalculationsException("??? Error using ==> issorted \n Unknown flag."));

                        //provera da li je drugi argument jednak sa 'rows'
                        string secStr = Helper.QCharNDimArrayToStringList(second).ElementAt(0);
                        secStr = secStr.ToLower();
                        
                        if (secStr.CompareTo("rows") == 0)
                        {
                            //fja radi za vektore ili 2-D matrice 
                            if (first.Dimension > 2)
                                throw (new NumericalCalculationsException("??? Error using ==> issorted \n X must be a 2-D matrix."));

                            //ako je prosledjen prazan niz on je uvek sortiran
                            if (first.Data.Length == 0)
                            {
                                val[0] = new QBoolean(true);
                                res.Add(new QBooleanNDimArray(val, 1, 1));
                                return res;
                            }
                            
                            //ako je u pitanju vektor POSTO JE NAVEDEN DRUGI ARGUMENT 'rows', taj vektor se posmatra kao jedan objekat (jedan red)
                            //posto se radi o samo jednom REDU on je uvek sortiran...
                            if (first.DimensionSize[0] == 1)
                            {
                                val[0] = new QBoolean(true);
                                res.Add(new QBooleanNDimArray(val, 1, 1));
                                return res;
                            }

                            //inace u pitanju je 2-D matrica
                            //sortiranost vrsta matrice je odredjena sortiranoscu prve kolone (tako radi Matlab)
                            for (int i = 0; i < first.DimensionSize[0] - 1; i++)
                                if (!Helper.lessThanOrEqualComplex(first.Data[i].Real, first.Data[i].Imag, first.Data[i + 1].Real, first.Data[i + 1].Imag))
                                {
                                    val[0] = new QBoolean(false);
                                    res.Add(new QBooleanNDimArray(val, 1, 1));
                                    return res;
                                }

                            //inace vrste su sortirane je sortiran
                            val[0] = new QBoolean(true);
                            res.Add(new QBooleanNDimArray(val, 1, 1));
                            return res;
                        }
                        else
                            throw (new NumericalCalculationsException("??? Error using ==> issorted \n Unknown flag."));
                    }
                    else
                        throw (new NumericalCalculationsException("??? Error using ==> issorted \n Unknown flag."));
                }
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'issorted' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja racuna kumulativni proizvod vektora, matrice, visedimenzionalnog niza...
        public List<IQLabValue> cumprod(List<IQLabValue> o)
        {
            //fja prima 1 ili 2 argumenta, u suprotnom - exception
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> cumprod \n Too many input arguments."));
            else if (o.Count < 1)
                throw (new NumericalCalculationsException("??? Error using ==> cumprod \n Not enough input arguments."));
            
            //ovako radi Matlab
            if(o.ElementAt(0).GetEnumType() == IQLabValueType.QCharNDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumprod' for input arguments of type 'char'."));
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumprod' for input arguments of type 'int64'."));
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumprod' for input arguments of type 'uint64'."));
            if(o.ElementAt(0).GetEnumType() == IQLabValueType.QBooleanNDimArray)
                throw (new NumericalCalculationsException("??? Error using ==> cumprod \n CUMPROD is only supported for floating point input."));

            if (Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));
                List<IQLabValue> res = new List<IQLabValue>();

                //provera da li je drugi argument odgovarajuci (pozitivan ceo broj)
                int second = 1;
                if (o.Count == 2)
                {
                    if ((o.ElementAt(1).GetEnumType() != IQLabValueType.QUInt64NDimArray)
                        && (o.ElementAt(1).GetEnumType() != IQLabValueType.QInt64NDimArray) && (o.ElementAt(1).GetEnumType() != IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Error using ==> cumprod \n Dimension argument must be a positive integer scalar within indexing range."));

                    QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(1));

                    //drugi argument mora biti skalar, ne sme biti kompleksan i mora biti pozitivan broj
                    if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || sec.Data[0].Real <= 0 || sec.Data[0].Real != (int)sec.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> cumprod \n Dimension argument must be a positive integer scalar within indexing range."));

                    second = (int)sec.Data[0].Real;
                }
                //ako je prosledjen samo jedan argument onda se za dimenziju uzima prva nonsingleton dimenzija
                else
                {
                    for(int i = 0; i < first.Dimension; i++)
                        if (first.DimensionSize[i] != 1)
                        {
                            second = i + 1;
                            break;
                        }
                }

                //ako je prosledjen prazan niz [] vraca se odgovarajuci prazan niz []
                if (first.Data.Length == 0)
                {
                    res.Add(new QFloat64NDimArray(first.DimensionSize));
                    return res;
                }

                //ako je prosledjena veca dimenzija => nema mnozenja! vraca se isti objekat!
                if (second > first.Dimension)
                {
                    res.Add(new QFloat64NDimArray(first));
                    return res;
                }

                //izracunavanje kumulativnog proizvoda
                //najpre se izracunava pomeraj kroz niz Data
                int move = 1;
                for (int i = 0; i < second - 1; i++)
                    move *= first.DimensionSize[i];

                //niz Data se deli na sekvencijalne delove dimenzije dimn i unutar tih delova se vrsi kumulativno mnozenje
                //racuna se dimn
                int dimn = move * first.DimensionSize[second - 1];

                QFloat64[] vals = new QFloat64[first.Data.Length];

                //mnozenje
                int lowerBound = -dimn;
                for (int i = 0; i < first.Data.Length; i++)
                {
                    if (i % dimn == 0)
                        lowerBound += dimn;

                    if (i - move < lowerBound)
                        vals[i] = new QFloat64(first.Data[i]);
                    else
                        vals[i] = new QFloat64(first.Data[i].Real * vals[i - move].Real - first.Data[i].Imag * vals[i - move].Imag,
                                                first.Data[i].Real * vals[i - move].Imag + first.Data[i].Imag * vals[i - move].Real);
                }

                res.Add(new QFloat64NDimArray(vals, first.DimensionSize));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumprod' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja racuna kumulativnu sumu vektora, matrice, visedimenzionalnog niza...
        public List<IQLabValue> cumsum(List<IQLabValue> o)
        {
            //fja prima 1 ili 2 argumenta, u suprotnom - exception
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> cumsum \n Too many input arguments."));
            else if (o.Count < 1)
                throw (new NumericalCalculationsException("??? Error using ==> cumsum \n Not enough input arguments."));
            
            //ovako radi Matlab
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QCharNDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumsum' for input arguments of type 'char'."));            
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumsum' for input arguments of type 'int64'."));
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumsum' for input arguments of type 'uint64'."));

            if (Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));
                List<IQLabValue> res = new List<IQLabValue>();

                //provera da li je drugi argument odgovarajuci (pozitivan ceo broj)
                int second = 1;
                if (o.Count == 2)
                {
                    if ((o.ElementAt(1).GetEnumType() != IQLabValueType.QUInt64NDimArray)
                        && (o.ElementAt(1).GetEnumType() != IQLabValueType.QInt64NDimArray) && (o.ElementAt(1).GetEnumType() != IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Error using ==> cumprod \n Dimension argument must be a positive integer scalar within indexing range."));

                    QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(1));

                    //drugi argument mora biti skalar, ne sme biti kompleksan i mora biti pozitivan broj
                    if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || sec.Data[0].Real <= 0 || sec.Data[0].Real != (int)sec.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> cumsum \n Dimension argument must be a positive integer scalar within indexing range."));

                    second = (int)sec.Data[0].Real;
                }
                //ako je prosledjen samo jedan argument onda se za dimenziju uzima prva nonsingleton dimenzija
                else
                {
                    for (int i = 0; i < first.Dimension; i++)
                        if (first.DimensionSize[i] != 1)
                        {
                            second = i + 1;
                            break;
                        }
                }

                //ako je prosledjen prazan niz vraca se prazan niz istih dimenzija
                if (first.Data.Length == 0)
                {
                    res.Add(new QFloat64NDimArray(first.DimensionSize));
                    return res;
                }

                //ako je prosledjena veca dimenzija => nema mnozenja! vraca se isti objekat!
                if (second > first.Dimension)
                {
                    res.Add(new QFloat64NDimArray(first));
                    return res;
                }

                //izracunavanje kumulativne sume
                //najpre se izracunava pomeraj kroz niz Data
                int move = 1;
                for (int i = 0; i < second - 1; i++)
                    move *= first.DimensionSize[i];

                //niz Data se deli na sekvencijalne delove dimenzije dimn i unutar tih delova se vrsi kumulativno sabiranje
                //najpre se racuna dimn
                int dimn = move * first.DimensionSize[second - 1];

                QFloat64[] vals = new QFloat64[first.Data.Length];

                //sabiranje
                int lowerBound = -dimn;
                for (int i = 0; i < first.Data.Length; i++)
                {
                    if (i % dimn == 0)
                        lowerBound += dimn;

                    if (i - move < lowerBound)
                        vals[i] = new QFloat64(first.Data[i]);
                    else
                        vals[i] = new QFloat64(first.Data[i].Real + vals[i - move].Real, first.Data[i].Imag + vals[i - move].Imag);
                }

                res.Add(new QFloat64NDimArray(vals, first.DimensionSize));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'cumsum' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja ispituje da li su svi elementi niza nenula ili true
        public List<IQLabValue> all(List<IQLabValue> o)
        {
            //fja prima 1 ili 2 argumenta, u suprotnom - exception
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> all \n Too many input arguments."));
            else if (o.Count < 1)
                throw (new NumericalCalculationsException("??? Error using ==> all \n Not enough input arguments."));

            //provera da li prvi argument moze da se konvertuje u QFloat64NDimArray
            if (Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));
                QBoolean[] vals;
                List<IQLabValue> res = new List<IQLabValue>();

                //provera da li je drugi argument odgovarajuci (pozitivan ceo broj)
                int second;
                if (o.Count == 2)
                {
                    if ((o.ElementAt(1).GetEnumType() != IQLabValueType.QInt64NDimArray)
                        && (o.ElementAt(1).GetEnumType() != IQLabValueType.QUInt64NDimArray) && (o.ElementAt(1).GetEnumType() != IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Error using ==> all \n Dimension argument must be a positive integer scalar within indexing range."));

                    QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(1));

                    if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || sec.Data[0].Real <= 0 || sec.Data[0].Real != (int)sec.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> all \n Dimension argument must be a positive integer scalar within indexing range."));

                    second = (int)sec.Data[0].Real;
                }
                //ako je prosledjen samo jedan argument onda se za dimenziju uzima prva nonsingleton dimenzija
                else
                {
                    second = 1;
                    for (int i = 0; i < first.Dimension; i++)
                        if (first.DimensionSize[i] != 1)
                        {
                            second = i + 1;
                            break;
                        }
                }

                //racunanje dimenzije rezultata
                //najpre se 'zanemaruju' dimenzije velicine 1 sdesna, tj. 2x3x1x1 = 2x3
                int resDimension = first.Dimension;
                while (resDimension > 2)
                    if (first.DimensionSize[resDimension - 1] == 1 || resDimension == second)
                        resDimension--;
                    else
                        break;

                //racunanje velicine dimenzija
                int[] resDimensionSize = new int[resDimension];
                for (int i = 0; i < resDimension; i++)
                    resDimensionSize[i] = first.DimensionSize[i];
                //sve dimenzije ostaju iste velicine osim one po kojoj se ispituje razlicitost od nule
                if (second - 1 < resDimension)
                    resDimensionSize[second - 1] = 1;

                //ako je prosledjen prazan niz vraca se true
                if (first.Data.Length == 0)
                {
                    if (second > first.Dimension)
                        res.Add(new QBooleanNDimArray(first.DimensionSize));
                    else if (first.Dimension == 2 && first.DimensionSize[0] == 0 && first.DimensionSize[1] == 0 && o.Count == 1)
                        res.Add(new QBooleanNDimArray(new QBoolean[] { new QBoolean(true) }, 1, 1));
                    else
                    {
                        int p = 1;
                        for (int i = 0; i < resDimensionSize.Length; i++)
                            p *= resDimensionSize[i];

                        QBoolean[] v = new QBoolean[p];
                        for (int i = 0; i < p; i++)
                            v[i] = new QBoolean(true);

                        res.Add(new QBooleanNDimArray(v, resDimensionSize));
                    }

                    return res;
                }

                //ako je prosledjena veca dimenzija
                if (second > first.Dimension)
                {
                    int n = first.Data.Length;
                    vals = new QBoolean[n];
                    for (int i = 0; i < n; i++)
                        vals[i] = new QBoolean((first.Data[i].Real == 0 && first.Data[i].Imag == 0) ? false : true);

                    res.Add(new QBooleanNDimArray(vals, first.DimensionSize));
                    return res;
                }

                //racuna se velicina Data niza rezultata 
                int valsSize = 1;
                for (int i = 0; i < first.Dimension; i++)
                    valsSize *= first.DimensionSize[i];
                valsSize /= first.DimensionSize[second - 1];
                vals = new QBoolean[valsSize];

                //racuna se pomeraj kroz niz Data
                int move = 1;
                for (int i = 0; i < second - 1; i++)
                    move *= first.DimensionSize[i];

                //niz Data se deli na sekvencijalne delove dimenzije dimn
                int dimn = move * first.DimensionSize[second - 1];

                int t = 0; //promenljiva za kretanje kroz niz vals

                for(int i = 0; i < first.Data.Length; i += dimn)
                    for(int j = i; j < i + move; j++)
                    {
                        int count = 0;
                        bool val = true;
                        for (int k = j; count < first.DimensionSize[second - 1]; count++, k += move)
                            //nadjen broj koji je jednak nuli
                            if ((first.Data[k].Real == 0) && (first.Data[k].Imag == 0))
                            {
                                val = false;
                                break;
                            }

                        vals[t++] = new QBoolean(val);
                    }

                res.Add(new QBooleanNDimArray(vals, resDimensionSize));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'all' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja ispituje da li je neki element niza nenula ili true (NaN se ignorise)
        public List<IQLabValue> any(List<IQLabValue> o)
        {
            //fja prima 1 ili 2 argumenta, u suprotnom - exception
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> any \n Too many input arguments."));
            else if (o.Count < 1)
                throw (new NumericalCalculationsException("??? Error using ==> any \n Not enough input arguments."));

            //provera da li prvi argument moze da se konvertuje u QFloat64NDimArray
            if (Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
            {
                QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));
                QBoolean[] vals;
                List<IQLabValue> res = new List<IQLabValue>();

                //provera da li je drugi argument odgovarajuci (pozitivan ceo broj)
                int second;
                if (o.Count == 2)
                {
                    if ((o.ElementAt(1).GetEnumType() != IQLabValueType.QInt64NDimArray)
                        && (o.ElementAt(1).GetEnumType() != IQLabValueType.QUInt64NDimArray) && (o.ElementAt(1).GetEnumType() != IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Error using ==> any \n Dimension argument must be a positive integer scalar within indexing range."));

                    QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(1));

                    if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || sec.Data[0].Real <= 0 || sec.Data[0].Real != (int)sec.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> any \n Dimension argument must be a positive integer scalar within indexing range."));

                    second = (int)sec.Data[0].Real;
                }
                //ako je prosledjen samo jedan argument onda se za dimenziju uzima prva nonsingleton dimenzija
                else
                {
                    second = 1;
                    for (int i = 0; i < first.Dimension; i++)
                        if (first.DimensionSize[i] != 1)
                        {
                            second = i + 1;
                            break;
                        }
                }

                //racunanje dimenzije rezultata
                //najpre se 'zanemaruju' dimenzije velicine 1 sdesna, tj. 2x3x1x1 = 2x3
                int resDimension = first.Dimension;
                while (resDimension > 2)
                    if (first.DimensionSize[resDimension - 1] == 1 || resDimension == second)
                        resDimension--;
                    else
                        break;

                //racunanje velicine dimenzija
                int[] resDimensionSize = new int[resDimension];
                for (int i = 0; i < resDimension; i++)
                    resDimensionSize[i] = first.DimensionSize[i];
                //sve dimenzije ostaju iste velicine osim one po kojoj se ispituje razlicitost od nule
                if (second - 1 < resDimension)
                    resDimensionSize[second - 1] = 1;

                //ako je prosledjen prazan niz vraca se false
                if (first.Data.Length == 0)
                {
                    if (second > first.Dimension)
                        res.Add(new QBooleanNDimArray(first.DimensionSize));
                    else if (first.Dimension == 2 && first.DimensionSize[0] == 0 && first.DimensionSize[1] == 0 && o.Count == 1)
                        res.Add(new QBooleanNDimArray(new QBoolean[] { new QBoolean(false) }, 1, 1));
                    else
                    {
                        int p = 1;
                        for (int i = 0; i < resDimensionSize.Length; i++)
                            p *= resDimensionSize[i];

                        QBoolean[] v = new QBoolean[p];
                        for (int i = 0; i < p; i++)
                            v[i] = new QBoolean(false);

                        res.Add(new QBooleanNDimArray(v, resDimensionSize));
                    }

                    return res;
                }

                //ako je prosledjena veca dimenzija
                if (second > first.Dimension)
                {
                    int n = first.Data.Length;
                    vals = new QBoolean[n];
                    for (int i = 0; i < n; i++)
                        vals[i] = new QBoolean((first.Data[i].Real == 0 && first.Data[i].Imag == 0) ? false : true);

                    res.Add(new QBooleanNDimArray(vals, first.DimensionSize));
                    return res;
                }

                //racuna se velicina Data niza rezultata 
                int valsSize = 1;
                for (int i = 0; i < first.Dimension; i++)
                    valsSize *= first.DimensionSize[i];
                valsSize /= first.DimensionSize[second - 1];
                vals = new QBoolean[valsSize];

                //racuna se pomeraj kroz niz Data
                int move = 1;
                for (int i = 0; i < second - 1; i++)
                    move *= first.DimensionSize[i];

                //niz Data se deli na sekvencijalne delove dimenzije dimn
                int dimn = move * first.DimensionSize[second - 1];

                int t = 0; //promenljiva za kretanje kroz niz vals

                for(int i = 0; i < first.Data.Length; i += dimn)
                    for(int j = i; j < i + move; j++)
                    {
                        int count = 0;
                        bool val = false;
                        for (int k = j; count < first.DimensionSize[second - 1]; count++, k += move)
                        {
                            //NaN se ignorise
                            if (double.IsNaN(first.Data[k].Real) || double.IsNaN(first.Data[k].Imag))
                                continue;

                            //nadjen broj koji je razlicit od nule
                            if ((first.Data[k].Real != 0) || (first.Data[k].Imag != 0))
                            {
                                val = true;
                                break;
                            }
                        }

                        vals[t++] = new QBoolean(val);
                    }

                res.Add(new QBooleanNDimArray(vals, resDimensionSize));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'any' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja vraca dimenzije (visedimenzionalnog) niza
        //prima 1 ili 2 argumenta, vraca jednu ili vise povratnih vrednosti
        public List<IQLabValue> size(List<IQLabValue> o)
        {
            if (o.Count > 3)
                throw (new NumericalCalculationsException("??? Error using ==> size \n Too many input arguments."));
            if (o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> size \n Not enough input arguments."));

            int outputSize = (int)(o.ElementAt(0) as QInt64NDimArray).Data[0].Real;
            if (outputSize == 0)
                outputSize++;

            QFloat64NDimArray first;
            if(Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                first = new QFloat64NDimArray(o.ElementAt(1));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'size' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            List<IQLabValue> res = new List<IQLabValue>();
            QFloat64[] vals;

            //ako je protrebno vratiti samo jedan izlazni argument
            if (outputSize == 1)
            {
                //ako nije navedena konkretna dimenzija (tj. drugi argument), onda se vraca vektor jednak DimenzionSize prvog argumenta
                if (o.Count == 2)
                {
                    vals = new QFloat64[first.Dimension];
                    for (int i = 0; i < first.Dimension; i++)
                        vals[i] = new QFloat64(first.DimensionSize[i]);

                    res.Add(new QFloat64NDimArray(vals, 1, first.Dimension));
                    return res;
                }

                //inace, potrebno je vratiti velicinu navedene dimenzije
                //ispitivanje korektnosti drugog argumenta
                if ((o.ElementAt(2).GetEnumType() != IQLabValueType.QInt64NDimArray) && (o.ElementAt(2).GetEnumType() != IQLabValueType.QUInt64NDimArray))
                    throw (new NumericalCalculationsException("??? Error using ==> size \n Dimension argument must be a positive integer scalar within indexing range."));

                QInt64NDimArray sec = new QInt64NDimArray(o.ElementAt(2));
                if(!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || (sec.Data[0].Real <= 0))
                    throw (new NumericalCalculationsException("??? Error using ==> size \n Dimension argument must be a positive integer scalar within indexing range."));

                long second = sec.Data[0].Real;
                
                //ako je prosledjena veca dimenzija vraca se 1, inace vraca se velicina trazene dimenzije
                res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64((second > first.Dimension) ? 1 : first.DimensionSize[second - 1]) }, 1, 1));
                return res;
            }

            //inace potrebno je vratiti vise povranih argumenata (velicine odgovarajucih dimenzija)
            //u tom slucaju drugi argument ne sme biti naveden
            if(o.Count == 3)
                throw (new NumericalCalculationsException("??? Error using ==> size \n Unknown command option."));

            int j;
            for (j = 0; (j < outputSize - 1) && (j < first.Dimension - 1); j++)
                res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64(first.DimensionSize[j]) }, 1, 1));

            int val = first.DimensionSize[j];
            for (int i = j + 1; i < first.Dimension; i++)
                val *= first.DimensionSize[i];

            res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64(val) }, 1, 1));

            for (j++; j < outputSize; j++)
                res.Add(new QFloat64NDimArray(new QFloat64[] { new QFloat64(1) }, 1, 1));

            return res;
        }

        //fja sortira elemente niza rastuce ili opadajuce
        //fja prihvata 1, 2 ili 3 argumenta, vraca jednu ili dve povratne vrednosti
        public List<IQLabValue> sort(List<IQLabValue> o)
        {
            if (o.Count > 4)
                throw (new NumericalCalculationsException("??? Error using ==> sort \n Too many input arguments."));
            if (o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> sort \n Not enough input arguments."));

            int outputSize = (int)(o.ElementAt(0) as QInt64NDimArray).Data[0].Real;
            if (outputSize == 0)
                outputSize++;
            if(outputSize > 2)
                throw (new NumericalCalculationsException("??? Error using ==> sort \n Too many output arguments."));

            QFloat64NDimArray f;
            int sortDimension;
            int mode; //mode moze biti 'ascend' (mode = 1) ili 'descend' (mode = 0)

            if(Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                f = new QFloat64NDimArray(o.ElementAt(1));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'sort' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            //ukoliko je prosledjen jedan argument racuna se sortDimension, a mode je 'ascend'
            if (o.Count == 2)
            {
                mode = 1;
                sortDimension = 1;
                for (int i = 0; i < f.Dimension; i++)
                    if (f.DimensionSize[i] != 1)
                    {
                        sortDimension = i + 1;
                        break;
                    }

            }
            //ukoliko su prosledjena dva argumenta to moze biti na sledeci nacin
            //sort(A, dim) ili sort(A, mode)
            else if (o.Count == 3)
            {
                //slucaj sort(A, dim), po defaultu mode = 'ascend'
                if ((o.ElementAt(2).GetEnumType() == IQLabValueType.QInt64NDimArray) || (o.ElementAt(2).GetEnumType() == IQLabValueType.QUInt64NDimArray) ||
                        (o.ElementAt(2).GetEnumType() == IQLabValueType.QFloat64NDimArray))
                {
                    QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(2));
                    if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || (sec.Data[0].Real <= 0) || (sec.Data[0].Real != (int)sec.Data[0].Real))
                        throw (new NumericalCalculationsException("??? Error using ==> sort \n DIM must be a positive integer."));

                    sortDimension = (int)sec.Data[0].Real;
                    mode = 1;
                }

                //slucaj sort(A, mode), sortiranje se vrsi po prvoj nonsingleton dimenziji
                else if (o.ElementAt(2).GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    QCharNDimArray sec = new QCharNDimArray(o.ElementAt(2));

                    if (!Helper.IsString(sec))
                        throw (new NumericalCalculationsException("??? Error using ==> sort \n sorting direction must be 'ascend' or 'descend'."));
                    string second = Helper.QCharNDimArrayToStringList(sec).ElementAt(0);
                    second = second.ToLower();

                    if (second.CompareTo("ascend") == 0)
                        mode = 1;
                    else if (second.CompareTo("descend") == 0)
                        mode = 0;
                    else
                        throw (new NumericalCalculationsException("??? Error using ==> sort \n sorting direction must be 'ascend' or 'descend'."));

                    //racuna se prva nonsingleton dimenzija
                    sortDimension = 1;
                    for (int i = 0; i < f.Dimension; i++)
                        if (f.DimensionSize[i] != 1)
                        {
                            sortDimension = i + 1;
                            break;
                        }
                }
                else
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n DIM must be a positive integer."));
            }
            //inace prosledjena su 3 argumenta i oni moraju biti u redosledu niz, dimenzija, mod
            else
            {
                QFloat64NDimArray sec;
                QCharNDimArray th;

                if (o.ElementAt(2).GetEnumType() != IQLabValueType.QInt64NDimArray && o.ElementAt(2).GetEnumType() != IQLabValueType.QUInt64NDimArray &&
                        o.ElementAt(2).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n DIM must be a positive integer."));
                
                sec = new QFloat64NDimArray(o.ElementAt(2));

                if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || (sec.Data[0].Real <= 0) || (sec.Data[0].Real != (int)sec.Data[0].Real))
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n DIM must be a positive integer."));

                sortDimension = (int)sec.Data[0].Real;

                if(o.ElementAt(3).GetEnumType() != IQLabValueType.QCharNDimArray)
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n sorting direction must be 'ascend' or 'descend'."));

                th = new QCharNDimArray(o.ElementAt(3));
                if (!Helper.IsString(th))
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n sorting direction must be 'ascend' or 'descend'."));

                string third = Helper.QCharNDimArrayToStringList(th).ElementAt(0);
                third = third.ToLower();
                
                if (third.CompareTo("ascend") == 0)
                    mode = 1;
                else if (third.CompareTo("descend") == 0)
                    mode = 0;
                else
                    throw (new NumericalCalculationsException("??? Error using ==> sort \n sorting direction must be 'ascend' or 'descend'."));
            }

            List<IQLabValue> res = new List<IQLabValue>();

            //ako je prosledjen niz karaktera, izlaz mora biti niz karaktera
            if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
            {
                QCharNDimArray first = new QCharNDimArray(o.ElementAt(1));

                //ako je potrebno vratiti samo jednu povratnu vrednost
                if (outputSize == 1)
                {
                    if (sortDimension > first.Dimension)
                    {
                        res.Add(new QCharNDimArray(first));
                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    QCharNDimArray sorted = new QCharNDimArray(first);

                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QChar temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QChar temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }

                    res.Add(sorted);
                    return res;
                }
                // ako je potrebno vratiti 2 povratne vrednosti
                else
                {
                    //odgovarajuci indeksi
                    QFloat64[] Ind = new QFloat64[first.Data.Length];

                    if (sortDimension > first.Dimension)
                    {
                        for (int i = 0; i < first.Data.Length; i++)
                            Ind[i] = new QFloat64(1);

                        res.Add(new QCharNDimArray(first));
                        res.Add(new QFloat64NDimArray(Ind, first.DimensionSize));

                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    //kreiranje niza indeksa
                    for(int i = 0; i < first.Data.Length; i += dimn)
                        for(int j = 0; j < move; j++)
                            for (int k = 0; k < first.DimensionSize[sortDimension - 1]; k++)
                                Ind[i + j + k * move] = new QFloat64(k + 1);

                    QCharNDimArray sorted = new QCharNDimArray(first);
                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QChar temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QChar temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }

                    res.Add(sorted);
                    res.Add(new QFloat64NDimArray(Ind, sorted.DimensionSize));
                    return res;
                }
            }
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QBooleanNDimArray)
            {
                QBooleanNDimArray first = new QBooleanNDimArray(o.ElementAt(1));

                //ako je potrebno vratiti samo jednu povratnu vrednost
                if (outputSize == 1)
                {
                    if (sortDimension > first.Dimension)
                    {
                        res.Add(new QBooleanNDimArray(first));
                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    QBooleanNDimArray sorted = new QBooleanNDimArray(first);

                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real == false && sorted.Data[l - move].Real == true); l -= move)
                                    {
                                        QBoolean temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real == true && sorted.Data[l - move].Real == false); l -= move)
                                    {
                                        QBoolean temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }

                    res.Add(sorted);
                    return res;
                }
                // ako je potrebno vratiti 2 povratne vrednosti
                else
                {
                    //odgovarajuci indeksi
                    QFloat64[] Ind = new QFloat64[first.Data.Length];

                    if (sortDimension > first.Dimension)
                    {
                        for (int i = 0; i < first.Data.Length; i++)
                            Ind[i] = new QFloat64(1);

                        res.Add(new QBooleanNDimArray(first));
                        res.Add(new QFloat64NDimArray(Ind, first.DimensionSize));

                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    //kreiranje niza indeksa
                    for (int i = 0; i < first.Data.Length; i += dimn)
                        for (int j = 0; j < move; j++)
                            for (int k = 0; k < first.DimensionSize[sortDimension - 1]; k++)
                                Ind[i + j + k * move] = new QFloat64(k + 1);

                    QBooleanNDimArray sorted = new QBooleanNDimArray(first);
                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real == false && sorted.Data[l - move].Real == true); l -= move)
                                    {
                                        QBoolean temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real == true && sorted.Data[l - move].Real == false); l -= move)
                                    {
                                        QBoolean temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }

                    res.Add(sorted);
                    res.Add(new QFloat64NDimArray(Ind, sorted.DimensionSize));
                    return res;
                }
            }
            else if(o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
            {
                QInt64NDimArray first = new QInt64NDimArray(o.ElementAt(1));

                //ako je potrebno vratiti samo jednu povratnu vrednost
                if (outputSize == 1)
                {
                    if (sortDimension > first.Dimension)
                    {
                        res.Add(new QInt64NDimArray(first));
                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    QInt64NDimArray sorted = new QInt64NDimArray(first);

                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }

                    res.Add(sorted);
                    return res;
                }
                // ako je potrebno vratiti 2 povratne vrednosti
                else
                {
                    //odgovarajuci indeksi
                    QFloat64[] Ind = new QFloat64[first.Data.Length];

                    if (sortDimension > first.Dimension)
                    {
                        for (int i = 0; i < first.Data.Length; i++)
                            Ind[i] = new QFloat64(1);

                        res.Add(new QInt64NDimArray(first));
                        res.Add(new QFloat64NDimArray(Ind, first.DimensionSize));

                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    //kreiranje niza indeksa
                    for (int i = 0; i < first.Data.Length; i += dimn)
                        for (int j = 0; j < move; j++)
                            for (int k = 0; k < first.DimensionSize[sortDimension - 1]; k++)
                                Ind[i + j + k * move] = new QFloat64(k + 1);

                    QInt64NDimArray sorted = new QInt64NDimArray(first);
                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }

                    res.Add(sorted);
                    res.Add(new QFloat64NDimArray(Ind, sorted.DimensionSize));
                    return res;
                }
            }
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray)
            {
                QUInt64NDimArray first = new QUInt64NDimArray(o.ElementAt(1));

                //ako je potrebno vratiti samo jednu povratnu vrednost
                if (outputSize == 1)
                {
                    if (sortDimension > first.Dimension)
                    {
                        res.Add(new QUInt64NDimArray(first));
                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    QUInt64NDimArray sorted = new QUInt64NDimArray(first);

                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QUInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QUInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }

                    res.Add(sorted);
                    return res;
                }
                // ako je potrebno vratiti 2 povratne vrednosti
                else
                {
                    //odgovarajuci indeksi
                    QFloat64[] Ind = new QFloat64[first.Data.Length];

                    if (sortDimension > first.Dimension)
                    {
                        for (int i = 0; i < first.Data.Length; i++)
                            Ind[i] = new QFloat64(1);

                        res.Add(new QUInt64NDimArray(first));
                        res.Add(new QFloat64NDimArray(Ind, first.DimensionSize));

                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    //kreiranje niza indeksa
                    for (int i = 0; i < first.Data.Length; i += dimn)
                        for (int j = 0; j < move; j++)
                            for (int k = 0; k < first.DimensionSize[sortDimension - 1]; k++)
                                Ind[i + j + k * move] = new QFloat64(k + 1);

                    QUInt64NDimArray sorted = new QUInt64NDimArray(first);
                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real < sorted.Data[l - move].Real); l -= move)
                                    {
                                        QUInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && (sorted.Data[l].Real > sorted.Data[l - move].Real); l -= move)
                                    {
                                        QUInt64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }

                    res.Add(sorted);
                    res.Add(new QFloat64NDimArray(Ind, sorted.DimensionSize));
                    return res;
                }
            }
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QFloat64NDimArray)
            {
                QFloat64NDimArray first = f;

                if (outputSize == 1)
                {
                    if (sortDimension > first.Dimension)
                    {
                        res.Add(new QFloat64NDimArray(first));
                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    QFloat64NDimArray sorted = new QFloat64NDimArray(first);

                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && Helper.lessThanComplex(sorted.Data[l].Real, sorted.Data[l].Imag, sorted.Data[l - move].Real, sorted.Data[l - move].Imag); l -= move)
                                    {
                                        QFloat64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && Helper.greaterThanComplex(sorted.Data[l].Real, sorted.Data[l].Imag, sorted.Data[l - move].Real, sorted.Data[l - move].Imag); l -= move)
                                    {
                                        QFloat64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);
                                    }
                                }
                        }

                    res.Add(sorted);
                    return res;
                }
                // ako je potrebno vratiti 2 povratne vrednosti
                else
                {
                    //odgovarajuci indeksi
                    QFloat64[] Ind = new QFloat64[first.Data.Length];

                    if (sortDimension > first.Dimension)
                    {
                        for (int i = 0; i < first.Data.Length; i++)
                            Ind[i] = new QFloat64(1);

                        res.Add(new QFloat64NDimArray(first));
                        res.Add(new QFloat64NDimArray(Ind, first.DimensionSize));

                        return res;
                    }

                    //sortiranje
                    //racuna se pomeraj kroz niz Data
                    int move = 1;
                    for (int i = 0; i < sortDimension - 1; i++)
                        move *= first.DimensionSize[i];

                    //niz Data se deli u sekvencijalne delove duzine dimn
                    int dimn = move * first.DimensionSize[sortDimension - 1];

                    //kreiranje niza indeksa
                    for (int i = 0; i < first.Data.Length; i += dimn)
                        for (int j = 0; j < move; j++)
                            for (int k = 0; k < first.DimensionSize[sortDimension - 1]; k++)
                                Ind[i + j + k * move] = new QFloat64(k + 1);

                    QFloat64NDimArray sorted = new QFloat64NDimArray(first);
                    //sortiranje u rastucem poretku
                    if (mode == 1)
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && Helper.lessThanComplex(sorted.Data[l].Real, sorted.Data[l].Imag, sorted.Data[l - move].Real, sorted.Data[l - move].Imag); l -= move)
                                    {
                                        QFloat64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }
                    //sortiranje u opadajucem poretku
                    else
                        for (int i = 0; i < sorted.Data.Length; i += dimn)
                        {
                            for (int j = 0; j < move; j++)
                                for (int k = i + j + move; k < i + dimn; k += move)
                                {

                                    for (int l = k; (l > i + j) && Helper.greaterThanComplex(sorted.Data[l].Real, sorted.Data[l].Imag, sorted.Data[l - move].Real, sorted.Data[l - move].Imag); l -= move)
                                    {
                                        QFloat64 temp = sorted.Data[l];
                                        sorted.SetDataElement(l, sorted.Data[l - move]);
                                        sorted.SetDataElement(l - move, temp);

                                        QFloat64 t = Ind[l];
                                        Ind[l] = Ind[l - move];
                                        Ind[l - move] = t;
                                    }
                                }
                        }

                    res.Add(sorted);
                    res.Add(new QFloat64NDimArray(Ind, sorted.DimensionSize));
                    return res;
                }
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'sort' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));
        }

        //fja permutuje elemente niza (matrice) --> uopstenje funkcije transponovanja '
        public List<IQLabValue> permute(List<IQLabValue> o)
        {
            //fja prima 2 argumenta: prvi je niz, a drugi je redosled po kojim dimenzijama se vrsi permutovanje
            if (o.Count > 2)
                throw (new NumericalCalculationsException("??? Error using ==> permute \n Too many input arguments."));
            if (o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> permute \n Not enough input arguments."));

            QFloat64NDimArray f;
            if (Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
                f = new QFloat64NDimArray(o.ElementAt(0));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'permute' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));

            //drugi argument ne sme biti karakterskog tipa
            if(o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
                throw (new NumericalCalculationsException("??? Error using ==> permute \n ORDER must contain double-precision indices."));

            QFloat64NDimArray second;
            if (Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                second = new QFloat64NDimArray(o.ElementAt(1));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'permute' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            //drugi argument mora sadrzati najmanje N brojeva koji predstavljaju dimenzije N-dimenzionalne matrice
            if(second.Data.Length < f.Dimension)
                throw (new NumericalCalculationsException("??? Error using ==> permute \n ORDER must have at least N elements for an N-D array."));

            //niz koji sadrzi prosledjene dimenzije
            //kreira se jer je dozvoljeno da se proslede float-ovi, kompleksni brojevi...
            //vrsi se kastovanje realnog dela u int i ta vrednost se smatra dimenzijom
            int[] dimensions = new int[second.Data.Length];
            int s = 0; //pomocna promenljiva koja sumira sve dimenzije i sluzi za proveru ispravnosti unetih dimenzija
            for (int i = 0; i < dimensions.Length; i++)
            {
                dimensions[i] = (int)second.Data[i].Real;

                if(dimensions[i] < 1 || dimensions[i] > dimensions.Length)
                    throw (new NumericalCalculationsException("??? Error using ==> permute \n ORDER contains an invalid permutation index."));
                
                //sumiranje svih dimenzija
                s += dimensions[i];
            }

            //ako je suma prvih n prirodnih brojeva razlicita od sume prvih first.Dimension prirodnih brojeva onda je doslo do ponavljanja dimenzija!!!
            if(s != (dimensions.Length * (dimensions.Length + 1)) / 2)
                throw (new NumericalCalculationsException("??? Error using ==> permute \n ORDER cannot contain repeated permutation indices."));

            //lista rezultata
            List<IQLabValue> res = new List<IQLabValue>();

            //dimenzije novog NDim niza...
            int[] newDimensionSize = new int[dimensions.Length];
            for (int i = 0; i < dimensions.Length; i++)
                newDimensionSize[i] = dimensions[i] - 1 < f.Dimension ? f.DimensionSize[dimensions[i] - 1] : 1;

            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QBooleanNDimArray)
            {
                QBooleanNDimArray first= new QBooleanNDimArray(o.ElementAt(0));
                QBoolean[] newArray = new QBoolean[first.Data.Length];
                int[] newCoords;
                int[] oldCoords = new int[dimensions.Length];

                //permutovanje
                for (int i = 0; i < newArray.Length; i++)
                {
                    newCoords = Helper.PositionToCoordinates(newDimensionSize, i);

                    for (int j = 0; j < dimensions.Length; j++)
                        oldCoords[dimensions[j] - 1] = newCoords[j];

                    int oldPos = Helper.CoordinatesToPosition(first.DimensionSize, oldCoords);

                    newArray[i] = new QBoolean(first.Data[oldPos]);
                }

                res.Add(new QBooleanNDimArray(newArray, newDimensionSize));
                return res;
            }
            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QCharNDimArray)
            {
                QCharNDimArray first = new QCharNDimArray(o.ElementAt(0));
                QChar[] newArray = new QChar[first.Data.Length];
                int[] newCoords;
                int[] oldCoords = new int[dimensions.Length];

                //permutovanje
                for (int i = 0; i < newArray.Length; i++)
                {
                    newCoords = Helper.PositionToCoordinates(newDimensionSize, i);

                    for (int j = 0; j < dimensions.Length; j++)
                        oldCoords[dimensions[j] - 1] = newCoords[j];

                    int oldPos = Helper.CoordinatesToPosition(first.DimensionSize, oldCoords);

                    newArray[i] = new QChar(first.Data[oldPos]);
                }

                res.Add(new QCharNDimArray(newArray, newDimensionSize));
                return res;
            }

            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QInt64NDimArray)
            {
                QInt64NDimArray first = new QInt64NDimArray(o.ElementAt(0));
                QInt64[] newArray = new QInt64[first.Data.Length];
                int[] newCoords;
                int[] oldCoords = new int[dimensions.Length];

                //permutovanje
                for (int i = 0; i < newArray.Length; i++)
                {
                    newCoords = Helper.PositionToCoordinates(newDimensionSize, i);

                    for (int j = 0; j < dimensions.Length; j++)
                        oldCoords[dimensions[j] - 1] = newCoords[j];

                    int oldPos = Helper.CoordinatesToPosition(first.DimensionSize, oldCoords);

                    newArray[i] = new QInt64(first.Data[oldPos]);
                }

                res.Add(new QInt64NDimArray(newArray, newDimensionSize));
                return res;
            }

            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QUInt64NDimArray)
            {
                QUInt64NDimArray first = new QUInt64NDimArray(o.ElementAt(0));
                QUInt64[] newArray = new QUInt64[first.Data.Length];
                int[] newCoords;
                int[] oldCoords = new int[dimensions.Length];

                //permutovanje
                for (int i = 0; i < newArray.Length; i++)
                {
                    newCoords = Helper.PositionToCoordinates(newDimensionSize, i);

                    for (int j = 0; j < dimensions.Length; j++)
                        oldCoords[dimensions[j] - 1] = newCoords[j];

                    int oldPos = Helper.CoordinatesToPosition(first.DimensionSize, oldCoords);

                    newArray[i] = new QUInt64(first.Data[oldPos]);
                }

                res.Add(new QUInt64NDimArray(newArray, newDimensionSize));
                return res;
            }

            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QFloat64NDimArray)
            {
                QFloat64NDimArray first = f;
                QFloat64[] newArray = new QFloat64[first.Data.Length];
                int[] newCoords;
                int[] oldCoords = new int[dimensions.Length];

                //permutovanje
                for (int i = 0; i < newArray.Length; i++)
                {
                    newCoords = Helper.PositionToCoordinates(newDimensionSize, i);

                    for (int j = 0; j < dimensions.Length; j++)
                        oldCoords[dimensions[j] - 1] = newCoords[j];

                    int oldPos = Helper.CoordinatesToPosition(first.DimensionSize, oldCoords);

                    newArray[i] = new QFloat64(first.Data[oldPos]);
                }

                res.Add(new QFloat64NDimArray(newArray, newDimensionSize));
                return res;
            }

            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'permute' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }

        //fja pronalazi nenula elemente prosledjenog niza i vraca linearne indekse tih elemenata
        //fja prima vise argumenata, a ima i vise povratnih vrednosti
        public List<IQLabValue> find(List<IQLabValue> o)
        {
            //fja prima 1, 2 ili tri argumenta
            if (o.Count > 4)
                throw (new NumericalCalculationsException("??? Error using ==> find \n Too many input arguments."));
            if (o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> find \n Not enough input arguments."));

            //fja vraca 1, 2 ili 3 povratne vrednosti
            //ako vraca 1 onda vraca linearne indekse nenula elemenata
            //ako vraca 2 onda vraca indekse vrste i kolone nenula elemenata
            //ako vraca 3 onda vraca indekse vrste i kolone kao i vrednost nenula elemanata
            int outputSize = (int)(o.ElementAt(0) as QInt64NDimArray).Data[0].Real;
            if (outputSize == 0)
                outputSize++;
            if(outputSize > 3)
                throw (new NumericalCalculationsException("??? Error using ==> find \n Too many output arguments."));

            QFloat64NDimArray f;    //prvi argument je niz
            int second;                //drugi argument je koliko najvise nenula elemenata se trazi
            int searchOpt;              //treci argument moze biti 'first' (searchOpt = 1) ili 'last' (searchOpt = 0)
            if (Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                f = new QFloat64NDimArray(o.ElementAt(1));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'find' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            //ako je prosledjen samo niz
            if (o.Count == 2)
            {
                second = f.Data.Length;
                searchOpt = 1;
            }
            //ako je prosledjen niz i maksimalan broj nenula elemenata
            else if (o.Count == 3)
            {
                if (!Helper.canConvertToEnum(o.ElementAt(2), IQLabValueType.QFloat64NDimArray))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Second argument must be a positive scalar integer."));
                
                QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(2));
                //drugi argument mora biti integer skalar, nekompleksan i pozitivan
                if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || (sec.Data[0].Real <= 0) || (sec.Data[0].Real != (int)sec.Data[0].Real))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Second argument must be a positive scalar integer."));

                second = (int)sec.Data[0].Real;
                searchOpt = 1;
            }
            //inace prosledjena su tri argumenta i moraju biti niz, br elemenata, searchOpt
            else
            {
                if (!Helper.canConvertToEnum(o.ElementAt(2), IQLabValueType.QFloat64NDimArray))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Second argument must be a positive scalar integer."));

                QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(2));
                //drugi argument mora biti integer skalar, nekompleksan i pozitivan
                if (!Helper.checkIfScalar(sec) || sec.Data[0].IsComplex() || (sec.Data[0].Real <= 0) || (sec.Data[0].Real != (int)sec.Data[0].Real))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Second argument must be a positive scalar integer."));

                second = (int)sec.Data[0].Real;
                
                //treci argument je tipa char i predstavlja opciju pretrage (da li pretraga ide od pocetka ili kraja niza)
                if(!Helper.canConvertToEnum(o.ElementAt(3), IQLabValueType.QCharNDimArray))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Option must be of char type."));

                QCharNDimArray th = new QCharNDimArray(o.ElementAt(3));
                if(!Helper.IsString(th))
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Invalid search option. Must be 'first' or 'last'."));
                
                string third = Helper.QCharNDimArrayToStringList(th).ElementAt(0);
                third = third.ToLower();

                if (third.CompareTo("first") == 0)
                    searchOpt = 1;
                else if (third.CompareTo("last") == 0)
                    searchOpt = 0;
                else
                    throw (new NumericalCalculationsException("??? Error using ==> find \n Invalid search option. Must be 'first' or 'last'."));
            }

            List<IQLabValue> res = new List<IQLabValue>();

            //ako je prosledjen prazan niz []
            if (f.Data.Length == 0)
            {
                //dodajemo odgovarajuci broj praznih matrica
                //ovako radi Matlab...
                int n = outputSize == 3 ? outputSize - 1 : outputSize;
                if (f.Dimension == 2 && f.DimensionSize[0] == 0 && f.DimensionSize[1] == 0)
                {
                    for (int i = 0; i < n; i++)
                        res.Add(new QFloat64NDimArray());
                    
                    if (outputSize == 3)
                    {
                        if (o.ElementAt(1).GetEnumType() == IQLabValueType.QBooleanNDimArray)
                            res.Add(new QBooleanNDimArray());
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
                            res.Add(new QCharNDimArray());
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
                            res.Add(new QInt64NDimArray());
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                            res.Add(new QUInt64NDimArray());
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                            res.Add(new QFloat64NDimArray());
                        else
                            throw (new NumericalCalculationsException("??? Undefined function or method 'find' for input arguments of type " +
                                        Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));
                    }
                }
                else
                {
                    for (int i = 0; i < n; i++)
                        res.Add(new QFloat64NDimArray(0, 1));

                    if (outputSize == 3)
                    {
                        if (o.ElementAt(1).GetEnumType() == IQLabValueType.QBooleanNDimArray)
                            res.Add(new QBooleanNDimArray(0, 1));
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
                            res.Add(new QCharNDimArray(0, 1));
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
                            res.Add(new QInt64NDimArray(0, 1));
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                            res.Add(new QUInt64NDimArray(0, 1));
                        else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                            res.Add(new QFloat64NDimArray(0, 1));
                        else
                            throw (new NumericalCalculationsException("??? Undefined function or method 'find' for input arguments of type " +
                                        Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));
                    }
                }

                return res;
            }

            //lista indeksa elemenata razlicitih od nule
            List<int> indices = new List<int>();

            //pretraga ide od pocetka
            if (searchOpt == 1)
            {
                for (int i = 0, count = 0; i < f.Data.Length && count < second; i++)
                    if (f.Data[i].Real == 0 && f.Data[i].Imag == 0)
                        continue;
                    else
                    {
                        indices.Add(i);
                        count++;
                    }
            }
            //inace pretraga ide od kraja
            else
            {
                for(int i = f.Data.Length - 1, count = 0; i >= 0 && count < second; i--)
                    if (f.Data[i].Real == 0 && f.Data[i].Imag == 0)
                        continue;
                    else
                    {
                        indices.Add(i);
                        count++;
                    }

                //obrcemo listu da bi indeksi bili u rastucem redosledu
                indices.Reverse();
            }

            //ako je potrebno vratiti jednu povratnu vrednost
            if (outputSize == 1)
            {
                QFloat64[] vals = new QFloat64[indices.Count];
                for (int i = 0; i < indices.Count; i++)
                    vals[i] = new QFloat64(indices[i] + 1);

                //ako je prosledjen red (vektor) povratna vrednost je takodje red (vektor)
                if (f.DimensionSize[0] == 1)
                {
                    res.Add(new QFloat64NDimArray(vals, 1, vals.Length));
                    return res;
                }

                //inace vraca se kolona
                res.Add(new QFloat64NDimArray(vals, vals.Length, 1));
                return res;
            }

            //outputSize > 1, racunamo indekse vrsta i kolona, a slucaj outputSize == 3 se razmatra kasnije
            QFloat64[] r = new QFloat64[indices.Count];
            QFloat64[] c = new QFloat64[indices.Count];

            for (int i = 0; i < indices.Count; i++)
            {
                int[] coords = Helper.PositionToCoordinates(f.DimensionSize, indices[i]);
                r[i] = new QFloat64(coords[0] + 1);

                //ako je prvi argument multidimenzionalni niz potrebno je transformisati koordinate
                //npr [x1, x2, ..., xn] se transformise u [x1, brKolona]
                // dakle u brKolona se sabijaju vise dimenzije...
                int t = coords[f.Dimension - 1];
                for (int j = f.Dimension - 2; j >= 1; j--)
                    t = t * f.DimensionSize[1] + coords[j];

                c[i] = new QFloat64(t + 1);
            }

            QFloat64NDimArray rows, columns;
            //ako je prosledjen red (vektor) povratne vrednosti su takodje redovi (vektori)
            if (f.DimensionSize[0] == 1)
            {
                rows = new QFloat64NDimArray(r, 1, r.Length);
                columns = new QFloat64NDimArray(c, 1, c.Length);
            }
            //inace povratne vrednosti su kolone
            else
            {
                rows = new QFloat64NDimArray(r, r.Length, 1);
                columns = new QFloat64NDimArray(c, c.Length, 1);
            }

            res.Add(rows);
            res.Add(columns);

            //potrebno je vratiti 3 povratne vrednosti
            //prve dve su izracunate (rows i columns), racunamo trecu (vrednost nenula elemenata)
            if (outputSize == 3)
            {
                if (o.ElementAt(1).GetEnumType() == IQLabValueType.QBooleanNDimArray)
                {
                    QBooleanNDimArray first = new QBooleanNDimArray(o.ElementAt(1));
                    QBoolean[] v = new QBoolean[indices.Count];
                    for (int i = 0; i < indices.Count; i++)
                        v[i] = new QBoolean(first.Data[indices[i]]);

                    if (first.DimensionSize[0] == 1)
                        res.Add(new QBooleanNDimArray(v, 1, v.Length));
                    else
                        res.Add(new QBooleanNDimArray(v, v.Length, 1));
                }

                else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    QCharNDimArray first = new QCharNDimArray(o.ElementAt(1));
                    QChar[] v = new QChar[indices.Count];
                    for (int i = 0; i < indices.Count; i++)
                        v[i] = new QChar(first.Data[indices[i]]);

                    if (first.DimensionSize[0] == 1)
                        res.Add(new QCharNDimArray(v, 1, v.Length));
                    else
                        res.Add(new QCharNDimArray(v, v.Length, 1));
                }

                else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                {
                    QUInt64NDimArray first = new QUInt64NDimArray(o.ElementAt(1));
                    QUInt64[] v = new QUInt64[indices.Count];
                    for (int i = 0; i < indices.Count; i++)
                        v[i] = new QUInt64(first.Data[indices[i]]);

                    if (first.DimensionSize[0] == 1)
                        res.Add(new QUInt64NDimArray(v, 1, v.Length));
                    else
                        res.Add(new QUInt64NDimArray(v, v.Length, 1));
                }

                else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
                {
                    QInt64NDimArray first = new QInt64NDimArray(o.ElementAt(1));
                    QInt64[] v = new QInt64[indices.Count];
                    for (int i = 0; i < indices.Count; i++)
                        v[i] = new QInt64(first.Data[indices[i]]);

                    if (first.DimensionSize[0] == 1)
                        res.Add(new QInt64NDimArray(v, 1, v.Length));
                    else
                        res.Add(new QInt64NDimArray(v, v.Length, 1));
                }

                else if(o.ElementAt(1).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                {
                    QFloat64NDimArray first = f;
                    QFloat64[] v = new QFloat64[indices.Count];
                    for (int i = 0; i < indices.Count; i++)
                        v[i] = new QFloat64(first.Data[indices[i]]);

                    if (first.DimensionSize[0] == 1)
                        res.Add(new QFloat64NDimArray(v, 1, v.Length));
                    else
                        res.Add(new QFloat64NDimArray(v, v.Length, 1));
                }

                else
                    throw (new NumericalCalculationsException("??? Undefined function or method 'find' for input arguments of type " +
                        Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));
            }

            return res;
        }

        //funkcija preoblikuje niz, npr niz 2x3x2 preoblikuje u 4x3...
        public List<IQLabValue> reshape(List<IQLabValue> o)
        {
            //fja prima 2 ili vise argumenata
            if(o.Count < 2)
                throw (new NumericalCalculationsException("??? Error using ==> reshape \n Not enough input arguments."));

            //provera ispravnosti prvog argumenta
            if(!Helper.canConvertToEnum(o.ElementAt(0), IQLabValueType.QFloat64NDimArray))
                throw (new NumericalCalculationsException("??? Undefined function or method 'reshape' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));

            QFloat64NDimArray first = new QFloat64NDimArray(o.ElementAt(0));

            int[] newDimensions;   //nove dimenzije
            int prod = 1;          //proizvod novih dimenzija (radi provere)
            
            //ako je prosledjen vektor novih dimenzija
            if (o.Count == 2)
            {
                if (!Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                    throw (new NumericalCalculationsException("??? Undefined function or method 'reshape' for input arguments of type " +
                        Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

                QFloat64NDimArray sec = new QFloat64NDimArray(o.ElementAt(1));
                if (sec.Data.Length < 2)
                    throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size vector must have at least two elements."));

                //drugi argument mora biti vektor
                if (sec.DimensionSize[0] != 1)
                    throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size vector must be a row vector with integer elements."));

                //cuvanje novih dimenzija u nizu
                newDimensions = new int[sec.Data.Length];

                for (int i = 0; i < newDimensions.Length; i++)
                {
                    if (sec.Data[i].IsComplex())
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size argument cannot be complex."));

                    if (sec.Data[i].Real < 0)
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size vector elements should be nonnegative."));

                    if (sec.Data[i].Real != (int)sec.Data[i].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size arguments must be real integers."));

                    newDimensions[i] = (int)sec.Data[i].Real;

                    prod *= newDimensions[i];
                }

                if(prod != first.Data.Length)
                    throw (new NumericalCalculationsException("??? Error using ==> reshape \n To RESHAPE the number of elements must not change."));
            }
            //inace prosledjene su dimenzije kao argumenti
            else
            {
                bool wasPlaceHolder = false;    //indikator da li je bilo nepoznatih dimenzija []
                int placeHolderIndex = 0;       //indeks nepoznate dimenzije

                newDimensions = new int[o.Count - 1];

                for (int i = 1; i < o.Count; i++)
                {
                    //provera da li je svaki argument korektno zadat
                    if(!Helper.canConvertToEnum(o.ElementAt(i), IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Undefined function or method 'reshape' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(i).GetEnumType()) + "."));

                    QFloat64NDimArray t = new QFloat64NDimArray(o.ElementAt(i));

                    //prosledjen je place holder []
                    if (t.Data.Length == 0)
                    {
                        //najvise moze biti prosledjena jedna nepoznata dimenzija
                        if (wasPlaceHolder)
                            throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size can only have one unknown dimension."));
                        else
                        {
                            placeHolderIndex = i - 1;
                            wasPlaceHolder = true;
                            continue;
                        }
                    }

                    if(!Helper.checkIfScalar(t))
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size arguments must be integer scalars."));

                    if(t.Data[0].IsComplex())
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size argument cannot be complex."));

                    if (t.Data[0].Real < 0)
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size vector elements should be nonnegative."));

                    if (t.Data[0].Real != (int)t.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Size arguments must be real integers."));

                    newDimensions[i - 1] = (int)t.Data[0].Real;
                    prod *= newDimensions[i - 1];
                }

                //racunamo nepoznatu dimenziju
                if (wasPlaceHolder)
                {
                    if (first.Data.Length % prod == 0)
                    {
                        newDimensions[placeHolderIndex] = first.Data.Length / prod;
                        prod *= newDimensions[placeHolderIndex];
                    }
                    else
                        throw (new NumericalCalculationsException("??? Error using ==> reshape \n Product of known dimensions, " +
                            prod + ", not divisible into total number of elements, " + first.Data.Length + "."));
                }

                if(prod != first.Data.Length)
                    throw (new NumericalCalculationsException("??? Error using ==> reshape \n To RESHAPE the number of elements must not change."));
            }

            List<IQLabValue> res = new List<IQLabValue>();

            //ako je prvi argument bio matrica karaktera i izlaz je istog tipa
            if (o.ElementAt(0).GetEnumType() == IQLabValueType.QCharNDimArray)
                res.Add(new QCharNDimArray((o.ElementAt(0) as QCharNDimArray).Data, newDimensions));
            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QBooleanNDimArray)
                res.Add(new QBooleanNDimArray((o.ElementAt(0) as QBooleanNDimArray).Data, newDimensions));
            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QInt64NDimArray)
                res.Add(new QInt64NDimArray((o.ElementAt(0) as QInt64NDimArray).Data, newDimensions));
            else if (o.ElementAt(0).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                res.Add(new QUInt64NDimArray((o.ElementAt(0) as QUInt64NDimArray).Data, newDimensions));
            else if(o.ElementAt(0).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                res.Add(new QFloat64NDimArray(first.Data, newDimensions));
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'reshape' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));

            return res;
        }

        //fja primenjuje 1-D digitalni filter
        //fja prima 3, 4 ili 5 argumenata, a vraca 1 ili 2 povratne vrednosti
        public List<IQLabValue> filter(List<IQLabValue> o)
        {
            //posto vraca vise povratnih vrednosti prvi element liste o je broj povratnih vrednosti pa je ukupan broj argumenata od 4 do 6
            if (o.Count > 6)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Too many input arguments."));
            if (o.Count < 4)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Not enough input arguments."));

            //broj povratnih vrednosti
            int outputSize = (int)(o.ElementAt(0) as QInt64NDimArray).Data[0].Real;
            if (outputSize == 0)
                outputSize++;
            if(outputSize > 2)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Too many output arguments."));
            
            //prvi argument moze biti tipa double ili bool
            if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray || o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'filter' for input arguments of type " +
                   Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            if (o.ElementAt(1).GetEnumType() != IQLabValueType.QFloat64NDimArray && o.ElementAt(1).GetEnumType() != IQLabValueType.QBooleanNDimArray)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Arguments must be single or double."));

            //prvi argument je vektor numeratora (numerator coefficients)
            QFloat64NDimArray b = new QFloat64NDimArray(o.ElementAt(1));
            if(b.Dimension > 2 || (b.DimensionSize[0] != 1 && b.DimensionSize[1] != 1) || b.Data.Length < 1)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n First two arguments must be vectors."));

            //drugi argument biti tipa double ili bool
            if (o.ElementAt(2).GetEnumType() == IQLabValueType.QInt64NDimArray || o.ElementAt(2).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'filter' for input arguments of type " +
                   Helper.PrintEnumName(o.ElementAt(2).GetEnumType()) + "."));

            if (o.ElementAt(2).GetEnumType() != IQLabValueType.QFloat64NDimArray && o.ElementAt(2).GetEnumType() != IQLabValueType.QBooleanNDimArray)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Arguments must be single or double."));

            //drugi argument je vektor denominatora (denominator coefficients)
            QFloat64NDimArray a = new QFloat64NDimArray(o.ElementAt(2));
            if (a.Dimension > 2 || (a.DimensionSize[0] != 1 && a.DimensionSize[1] != 1) || a.Data.Length < 1)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n First two arguments must be vectors."));

            //prvi element vektora a ne sme biti 0
            if(a.Data[0].Real == 0 && a.Data[0].Imag == 0)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n First denominator filter coefficient must be non-zero."));

            if (o.ElementAt(3).GetEnumType() == IQLabValueType.QInt64NDimArray || o.ElementAt(3).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                throw (new NumericalCalculationsException("??? Undefined function or method 'filter' for input arguments of type " +
                   Helper.PrintEnumName(o.ElementAt(3).GetEnumType()) + "."));

            //treci argument je matrica podataka koja se filteruje i mora biti tipa double
            if (o.ElementAt(3).GetEnumType() != IQLabValueType.QFloat64NDimArray && o.ElementAt(3).GetEnumType() != IQLabValueType.QBooleanNDimArray)
                throw (new NumericalCalculationsException("??? Error using ==> filter \n Arguments must be single or double."));

            //matrica podataka koja se filteruje
            QFloat64NDimArray array = new QFloat64NDimArray(o.ElementAt(3));

            int n = Math.Max(a.Data.Length, b.Data.Length) - 1;

            QFloat64[] zi = new QFloat64[n];    //inicijalni uslov u slucaju da je zadat kao vektor
            QFloat64NDimArray ziArray = null;   //inicijalni uslov moze biti zadat kao visedimenzionalni niz
            int dim = 1;                        //dimenzija po kojoj se vrsi filtriranje

            //ako su prosledjena 3 argumenta, ostali se racunaju...
            if (o.Count == 4)
            {
                //filtriranje se vrsi po prvoj nonsingleton dimenziji
                for(int i = 0; i < array.Dimension; i++)
                    if (array.DimensionSize[i] != 1)
                    {
                        dim = i + 1;
                        break;
                    }

                for (int i = 0; i < n; i++)
                    zi[i] = new QFloat64(0, 0);
            }
            //prosledjeno je najmanje 4 argumenata
            else
            {
                //da li je prosledjen i 5. argument?
                if (o.Count == 6)
                {
                    //ako je prosledjen i peti argument (tj. dimenzija)
                    if (o.ElementAt(5).GetEnumType() != IQLabValueType.QInt64NDimArray && o.ElementAt(5).GetEnumType() != IQLabValueType.QUInt64NDimArray &&
                        o.ElementAt(5).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                        throw (new NumericalCalculationsException("??? Undefined function or method 'filter' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(5).GetEnumType()) + "."));

                    if (o.ElementAt(5).GetEnumType() == IQLabValueType.QCharNDimArray)
                        throw (new NumericalCalculationsException("??? Error using ==> filter \n Arguments must be single or double."));

                    //dimenzija mora biti skalar, nekompleksan i pozitivan broj
                    QFloat64NDimArray arg5 = new QFloat64NDimArray(o.ElementAt(5));
                    if (!Helper.checkIfScalar(arg5) || arg5.Data[0].IsComplex() || arg5.Data[0].Real <= 0 || arg5.Data[0].Real != (int)arg5.Data[0].Real)
                        throw (new NumericalCalculationsException("??? Error using ==> filter \n Dimension argument must be a positive integer scalar within indexing range."));

                    dim = (int)arg5.Data[0].Real;

                    //!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!
                    //MATLAB SE CUDNO PONASA KADA MU SE PROSLEDI VECA DIMENZIJA OD array.Dimenzion PA SE ZATO OVDE ISPALJUJE IZUZETAK !!!
                    if(dim > array.Dimension)
                        throw (new NumericalCalculationsException("??? Error using ==> filter \n Dimension argument must be a positive integer scalar within indexing range."));
                    //!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!

                }
                //inace, filtriranje se vrsi po prvoj nonsingleton dimenziji
                else
                    for (int i = 0; i < array.Dimension; i++)
                        if (array.DimensionSize[i] != 1)
                        {
                            dim = i + 1;
                            break;
                        }
                
                //vracamo se na 4. argument
                //4. argument je zi
                if (o.ElementAt(4).GetEnumType() == IQLabValueType.QInt64NDimArray || o.ElementAt(4).GetEnumType() == IQLabValueType.QUInt64NDimArray)
                    throw (new NumericalCalculationsException("??? Undefined function or method 'filter' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(5).GetEnumType()) + "."));

                if (o.ElementAt(4).GetEnumType() != IQLabValueType.QFloat64NDimArray && o.ElementAt(4).GetEnumType() != IQLabValueType.QBooleanNDimArray)
                    throw (new NumericalCalculationsException("??? Error using ==> filter \n Arguments must be single or double."));

                QFloat64NDimArray arg4 = new QFloat64NDimArray(o.ElementAt(4));
                //ako je prosledjen prazan vektor zi, smatra se da su mu elementi nule
                if (arg4.Data.Length == 0)
                    for (int i = 0; i < n; i++)
                        zi[i] = new QFloat64(0, 0);
                else
                //inace prosledjen je vektor zi
                {
                    //vektor zi mora da bude odgovarajucih dimenzija
                    //mora biti vektor duzine max(len(a), len(b)) - 1
                    if (arg4.Dimension == 2 && (arg4.DimensionSize[0] == 1 || arg4.DimensionSize[1] == 1))
                    {
                        if (arg4.Data.Length != n)
                            throw (new NumericalCalculationsException("??? Error using ==> filter \n Initial conditions must be a vector of length max(length(a),length(b))-1, or an array with the leading dimension of size max(length(a),length(b))-1 and with remaining dimensions matching those of x."));

                        for (int i = 0; i < n; i++)
                            zi[i] = new QFloat64(arg4.Data[i].Real, arg4.Data[i].Imag);
                    }
                    //ili mora biti visedimenzioni niz cija je prva dimenzija velicine n = max(len(a), len(b)) - 1
                    //a ostale dimenzije se podudaraju sa dimenzijama prosledjenog niza (array) kada se iz dimenzija niza array 'izbaci' dimenzija po kojoj se filtira
                    //tj, ako je niz array velicine 2x3x2 i ako se filtrira po drugoj dimenziji onda visedimenzionalni niz zi mora biti dimenzija nx2x2...
                    else if (arg4.DimensionSize[0] == n)
                    {
                        int i;
                        for (i = 1; i < array.Dimension && i < dim; i++)
                            if (arg4.DimensionSize[i] != array.DimensionSize[i - 1])
                                throw (new NumericalCalculationsException("??? Error using ==> filter \n Initial conditions must be a vector of length max(length(a),length(b))-1, or an array with the leading dimension of size max(length(a),length(b))-1 and with remaining dimensions matching those of x."));
                        for(; i < array.Dimension; i++)
                            if (arg4.DimensionSize[i] != array.DimensionSize[i])
                                throw (new NumericalCalculationsException("??? Error using ==> filter \n Initial conditions must be a vector of length max(length(a),length(b))-1, or an array with the leading dimension of size max(length(a),length(b))-1 and with remaining dimensions matching those of x."));

                        ziArray = arg4;
                    }
                    else
                        throw (new NumericalCalculationsException("??? Error using ==> filter \n Initial conditions must be a vector of length max(length(a),length(b))-1, or an array with the leading dimension of size max(length(a),length(b))-1 and with remaining dimensions matching those of x."));
                }
            }

            //lista povratnih vrednosti
            List<IQLabValue> res = new List<IQLabValue>();

            //ako je prosledjen prazan niz
            if (array.Data.Length == 0)
            {
                res.Add(new QFloat64NDimArray(array.DimensionSize));

                if (outputSize == 2)
                {
                    int[] emptySize = (int[])array.DimensionSize.Clone();
                    emptySize[0] = 1;
                    res.Add(new QFloat64NDimArray(emptySize));
                }

                return res;
            }
            
            //prva povratna vrednost
            QFloat64NDimArray out1 = new QFloat64NDimArray(array.DimensionSize);
            for (int i = 0; i < out1.Data.Length; i++)
                out1.SetDataElement(i, new QFloat64(0, 0));

            int move;       //pomeraj kroz niz Data
            int dimSize;    //velicina dimenzije po kojoj se vrsi filtriranje
            int dimn;       //niz Data se deli na sekvencijalne nizove duzine dimn
            
            /*int ziLen;      //duzina vektora zi
            //OVAJ SLUCAJ JE ISKLJUCEN ZBOG CUDNOG PONASANJA MATLAB-a!!!!
            if (dim > array.Dimension)
            {
                move = 1;
                dimn = 1;
                dimSize = 1;
                ziLen = 1;
            }
            else
            //racuna se move i dimn
            {
                move = 1;
                for (int i = 0; i < dim - 1; i++)
                    move *= array.DimensionSize[i];

                dimn = move * array.DimensionSize[dim - 1];

                dimSize = array.DimensionSize[dim - 1];

                ziLen = n;
            }*/

            //racuna se move i dimn
            move = 1;
            for (int i = 0; i < dim - 1; i++)
                move *= array.DimensionSize[i];

            dimn = move * array.DimensionSize[dim - 1];

            dimSize = array.DimensionSize[dim - 1];

            //razlikuju se 2 slucaja: ako je prosledjen vektor zi ili ako je prosledjena visedimenzioni niz zi (ziArray)

            //pomocni nizovi za izdvajanje odgovarajucih elemenata matrica array i out1
            QFloat64[] xPom = new QFloat64[dimSize];
            QFloat64[] yPom = new QFloat64[dimSize];

            int p = 0; //indeks za kretanje kroz ziArray

            for (int i = 0; i < array.Data.Length; i += dimn)
                for (int j = 0; j < move; j++)
                {
                    //izdvajanje odgovarajucih nizova duz dimenzije dim iz ulaznog niza (array) i izlaznog niza (out1)
                    for (int k = 0; k < dimSize; k++)
                    {
                        xPom[k] = new QFloat64(array.Data[i + j + k * move]);
                        yPom[k] = new QFloat64(out1.Data[i + j + k * move]);
                    }

                    //slucaj kada je prosledjen ziArray
                    //iz ziArray se izdvaja odgovarajuci podniz i smesta u zi
                    if(ziArray != null)
                    {
                        for (int k = 0; k < n; k++)
                            zi[k] = new QFloat64(ziArray.Data[p * n + k]);
                        p++;
                    }

                    //racunanje prve izlazne vrednosti
                    for (int k = 0; k < dimSize; k++)
                    {
                        QFloat64 z = Helper.CalculateZ(xPom, yPom, zi, b.Data, a.Data, 0, k - 1, n);

                        double R = b.Data[0].Real * xPom[k].Real - b.Data[0].Imag * xPom[k].Imag + z.Real;
                        double I = b.Data[0].Real * xPom[k].Imag + b.Data[0].Imag * xPom[k].Real + z.Imag;

                        double Real = (R * a.Data[0].Real + I * a.Data[0].Imag) / (a.Data[0].Real * a.Data[0].Real + a.Data[0].Imag * a.Data[0].Imag);
                        double Imag = (-R * a.Data[0].Imag + I * a.Data[0].Real) / (a.Data[0].Real * a.Data[0].Real + a.Data[0].Imag * a.Data[0].Imag);

                        yPom[k] = new QFloat64(Real, Imag);

                        out1.SetDataElement(i + j + k * move, new QFloat64(Real, Imag));
                    }
                }

            res.Add(out1);

            //ako je potrebno racunati zf
            if (outputSize == 2)
            {
                //dimenzija niza zf
                int[] zfDimenzionSize;
                //OVAJ SLUCAJ JE ISKLJUCEN ZBOG CUDNOG PONASANJA MATLAB-a!!!
                /*if (dim > array.Dimension)
                {
                    zfDimenzionSize = new int[array.Dimension + 1];
                    zfDimenzionSize[0] = n;
                    for (int i = 0; i < array.Dimension; i++)
                        zfDimenzionSize[i + 1] = array.DimensionSize[i];
                }
                else
                {
                    zfDimenzionSize = new int[array.Dimension];
                    zfDimenzionSize[0] = n;
                    for (int i = 0; i < dim - 1; i++)
                        zfDimenzionSize[i + 1] = array.DimensionSize[i];
                    for (int i = dim; i < array.Dimension; i++)
                        zfDimenzionSize[i] = array.DimensionSize[i];
                }*/

                zfDimenzionSize = new int[array.Dimension];
                zfDimenzionSize[0] = n;
                for (int i = 0; i < dim - 1; i++)
                    zfDimenzionSize[i + 1] = array.DimensionSize[i];
                for (int i = dim; i < array.Dimension; i++)
                    zfDimenzionSize[i] = array.DimensionSize[i];

                QFloat64NDimArray zf = new QFloat64NDimArray(zfDimenzionSize);

                p = 0;          //indeks za kretanje kroz ziArray (ako je prosledjen)
                int count = 0;  //indeks za kretanje kroz Data niz zf
                for (int i = 0; i < array.Data.Length; i += dimn)
                    for (int j = 0; j < move; j++)
                    {
                        for (int k = 0; k < dimSize; k++)
                        {
                            yPom[k] = new QFloat64(out1.Data[i + j + k * move]);
                            xPom[k] = new QFloat64(array.Data[i + j + k * move]);
                        }

                        //slucaj kada je prosledjen ziArray
                        //iz ziArray se izdvaja odgovarajuci podniz i smesta u zi
                        if (ziArray != null)
                        {
                            for (int k = 0; k < n; k++)
                                zi[k] = new QFloat64(ziArray.Data[p * n + k]);
                            p++;
                        }

                        for (int k = 0; k < n; k++)
                        {
                            QFloat64 t = Helper.CalculateZ(xPom, yPom, zi, b.Data, a.Data, k, dimSize - 1, n);
                            double Real = (t.Real * a.Data[0].Real + t.Imag * a.Data[0].Imag) / (a.Data[0].Real * a.Data[0].Real + a.Data[0].Imag * a.Data[0].Imag);
                            double Imag = (-t.Real * a.Data[0].Imag + t.Imag * a.Data[0].Real) / (a.Data[0].Real * a.Data[0].Real + a.Data[0].Imag * a.Data[0].Imag);

                            zf.SetDataElement(count++, new QFloat64(Real, Imag));
                        }
                    }

                res.Add(zf);
            }

            return res;
        }

        //fja ili operator colon (:) ponasa se dvostruko, tj. razlikuju se 2 slucaja
        //prvi  --> a:c ili a : b : c
        //drugi --> A(:, j), A(i, :), A(:) i slicno
        public List<IQLabValue> colon(List<IQLabValue> o)
        {
            if (o.Count < 3)
                throw new NumericalCalculationsException("??? Error using ==> colon \n Not enough input arguments.");

            //indikator koji ce omoguciti razdvajanje slucajeva: 1 --> prvi slucaj, 2 --> drugi slucaj
            QFloat64NDimArray ind = new QFloat64NDimArray(o.ElementAt(0));
            double indicator = ind.Data[0].Real;

            List<IQLabValue> res = new List<IQLabValue>();

            //prvi slucaj --> a : b : c
            if (indicator == 1)
            {
                if (o.Count > 4)
                    throw new NumericalCalculationsException("??? Error using ==> colon \n Too many input arguments.");

                double b;   // korak b
                if (o.Count == 3)
                    b = 1;
                else
                {
                    if (o.ElementAt(2).GetEnumType() == IQLabValueType.QInt32NDimArray || o.ElementAt(2).GetEnumType() == IQLabValueType.QInt32NDimArray)
                        throw new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(2).GetEnumType()) + ".");

                    if(o.ElementAt(2).GetEnumType() == IQLabValueType.QCharNDimArray)
                        throw new NumericalCalculationsException("??? Error using ==> colon \n For colon operator with char operands, first and last operands must be char.");

                    if(!Helper.canConvertToEnum(o.ElementAt(2), IQLabValueType.QFloat64NDimArray))
                        throw new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(2).GetEnumType()) + ".");

                    QFloat64NDimArray t = new QFloat64NDimArray(o.ElementAt(2));
                    b = t.Data[0].Real;
                }

                //a i c su tipa char
                if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray && o.ElementAt(o.Count - 1).GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    char a = (o.ElementAt(1) as QCharNDimArray).Data[0].Real;
                    char c = (o.ElementAt(o.Count - 1) as QCharNDimArray).Data[0].Real;

                    if ((a < c && b <= 0) || (a > c && b >=0))
                    {
                        res.Add(new QCharNDimArray(1, 0));
                        return res;
                    }

                    int count = (int)Math.Abs((c - a) / b) + 1;
                    QChar[] vals = new QChar[count];
                    for (int i = 0; i < count; i++)
                        vals[i] = new QChar((char)(a + i * b));

                    res.Add(new QCharNDimArray(vals, 1, count));
                }
                //ni a ni c nisu tipa char
                else if (o.ElementAt(1).GetEnumType() != IQLabValueType.QCharNDimArray && o.ElementAt(o.Count - 1).GetEnumType() != IQLabValueType.QCharNDimArray)
                {
                    

                    if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray ||
                        o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray || !Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

                    QFloat64NDimArray a1 = new QFloat64NDimArray(o.ElementAt(1));

                    if (o.ElementAt(o.Count - 1).GetEnumType() == IQLabValueType.QInt64NDimArray ||
                        o.ElementAt(o.Count - 1).GetEnumType() == IQLabValueType.QUInt64NDimArray || !Helper.canConvertToEnum(o.ElementAt(o.Count - 1), IQLabValueType.QFloat64NDimArray))
                        throw (new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(o.Count - 1).GetEnumType()) + "."));

                    QFloat64NDimArray c1 = new QFloat64NDimArray(o.ElementAt(o.Count - 1));

                    double a = a1.Data[0].Real;
                    double c = c1.Data[0].Real;

                    if ((a < c && b <= 0) || (a > c && b >= 0))
                    {
                        res.Add(new QFloat64NDimArray(1, 0));
                        return res;
                    }

                    int count = (int)Math.Abs((c - a) / b) + 1;
                    QFloat64[] vals = new QFloat64[count];
                    for (int i = 0; i < count; i++)
                        vals[i] = new QFloat64(a + i * b);

                    res.Add(new QFloat64NDimArray(vals, 1, count));
                }
                else
                    throw new NumericalCalculationsException("??? Error using ==> colon \n For colon operator with char operands, first and last operands must be char.");

                return res;
            }

            //drugi slucaj --> A(:, j), A(i, :), A(:) i slicno

            //prvi argument posle indikatora je matrica
            if(!Helper.canConvertToEnum(o.ElementAt(1), IQLabValueType.QFloat64NDimArray))
                throw (new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                            Helper.PrintEnumName(o.ElementAt(1).GetEnumType()) + "."));

            QFloat64NDimArray arrayTemp = new QFloat64NDimArray(o.ElementAt(1));
            QCharNDimArray twoPoint;
            QFloat64NDimArray number;
            //svaki argument posle matrice odredjuje moguce vrednosti koordinata
            //za svaku koordinatu cuvaju se moguce vrednosti
            //posto imamo niz koordinata, a jedna koordinata moze imati vise mogucih vrednosti, one ce se cuvati u nizu
            //dakle, potreban je niz nizova i za tu potrebu se koristi valuesForCoordinates
            int[][] valuesForCoordinates = new int[o.Count - 2][];
            int dimLenght;
            int dimSize;
                
            //citanje ostalih argumenata osim poslednjeg
            for (int i = 0; i < valuesForCoordinates.Length - 1; i++)
            {
                if (o.ElementAt(i + 2).GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    twoPoint = o.ElementAt(i + 2) as QCharNDimArray;
                    if (!Helper.checkIfScalar(twoPoint) || twoPoint.Data[0].Real != ':')
                        throw new NumericalCalculationsException("??? Index exceeds matrix dimensions.");

                    dimLenght = i < arrayTemp.Dimension ? arrayTemp.DimensionSize[i] : 1;
                    valuesForCoordinates[i] = new int[dimLenght];

                    for (int ii = 0; ii < valuesForCoordinates[i].Length; ii++)
                        valuesForCoordinates[i][ii] = ii;
                }
                else if (Helper.canConvertToEnum(o.ElementAt(i + 2), IQLabValueType.QFloat64NDimArray))
                {
                    number = new QFloat64NDimArray(o.ElementAt(i + 2));

                    valuesForCoordinates[i] = new int[number.Data.Length];
                    dimSize = i < arrayTemp.Dimension ? arrayTemp.DimensionSize[i] : 1;
                    for (int ii = 0; ii < number.Data.Length; ii++)
                    {
                        if(number.Data[ii].IsComplex() || number.Data[ii].Real <= 0 || number.Data[ii].Real != (int)number.Data[ii].Real)
                            throw new NumericalCalculationsException("??? Subscript indices must either be real positive integers or logicals.");

                        if(number.Data[ii].Real > dimSize)
                            throw new NumericalCalculationsException("??? Index exceeds matrix dimensions.");

                        valuesForCoordinates[i][ii] = (int)number.Data[ii].Real - 1;
                    }                        
                }
                else
                    throw new NumericalCalculationsException("??? Subscript indices must either be real positive integers or logicals.");
            }
            //poslednji argument se cita odvojeno zbog sledeceg
            //ako je data matrica A dimenzija 2x3x2 kada se unese A(2, :) matrica A se posmatra kao 2 x 6 pa su moguce vrednosti za poslednju dimenziju 1, 2, 3, 4, 5, 6
            //dakle, vrsi se 'sabijanje' preostalih dimenzija u jednu
            int last = valuesForCoordinates.Length - 1;
            if (o.ElementAt(last + 2).GetEnumType() == IQLabValueType.QCharNDimArray)
            {
                twoPoint = o.ElementAt(last + 2) as QCharNDimArray;
                if (!Helper.checkIfScalar(twoPoint) || twoPoint.Data[0].Real != ':')
                    throw new NumericalCalculationsException("??? Index exceeds matrix dimensions.");

                int dim = 1;
                for (int ii = last; ii < arrayTemp.Dimension; ii++)
                    dim *= arrayTemp.DimensionSize[ii];

                valuesForCoordinates[last] = new int[dim];

                for (int ii = 0; ii < valuesForCoordinates[last].Length; ii++)
                    valuesForCoordinates[last][ii] = ii;
            }
            else if (Helper.canConvertToEnum(o.ElementAt(last + 2), IQLabValueType.QFloat64NDimArray))
            {
                number = new QFloat64NDimArray(o.ElementAt(last + 2));

                int dim = 1;
                for (int ii = last; ii < arrayTemp.Dimension; ii++)
                    dim *= arrayTemp.DimensionSize[ii];

                valuesForCoordinates[last] = new int[number.Data.Length];
                for (int ii = 0; ii < number.Data.Length; ii++)
                {
                    if (number.Data[ii].IsComplex() || number.Data[ii].Real <= 0 || number.Data[ii].Real != (int)number.Data[ii].Real)
                        throw new NumericalCalculationsException("??? Subscript indices must either be real positive integers or logicals.");

                    if (number.Data[ii].Real > dim)
                        throw new NumericalCalculationsException("??? Index exceeds matrix dimensions.");

                    valuesForCoordinates[last][ii] = (int)number.Data[ii].Real - 1;
                }

            }
            else
                throw new NumericalCalculationsException("??? Subscript indices must either be real positive integers or logicals.");


            //pomocni niz dimenzija jer se na kraju novih dimenzija mogu nalaziti nepotrebne jedinice
            int[] newDimPom = new int[valuesForCoordinates.Length];
            int[] newDim;

            int[] coords = new int[valuesForCoordinates.Length];
            //indeksi kojima se krece kroz valuesForDimension i na taj nacin dobijaju moguce vrednosti za datu dimenziju
            int[] indexPositions = new int[valuesForCoordinates.Length];

            int valsSize = 1;

            for (int j = 0; j < coords.Length; j++)
            {
                coords[j] = valuesForCoordinates[j][indexPositions[j]];
                newDimPom[j] = valuesForCoordinates[j].Length;
                valsSize *= valuesForCoordinates[j].Length;
            }

            //brisanje jedinica s desne strane u dimenziji nove matrice
            int n = newDimPom.Length;
            while (n > 2 && newDimPom[n - 1] == 1)
                n--;

            if (n < 2)
            {
                newDim = new int[2];
                newDim[0] = newDimPom[0];
                newDim[1] = 1;
            }
            else
            {
                newDim = new int[n];
                for (int j = 0; j < n; j++)
                    newDim[j] = newDimPom[j];
            }

            //ako je u pitanju matrica karaktera
            if (o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray)
            {
                QCharNDimArray array = new QCharNDimArray(o.ElementAt(1));
                QChar[] vals = new QChar[valsSize];
                int v = 0;
                //kretanje kroz valuesForDimension. Prodje se kroz sve moguce vrednosti...
                int prenos = 0;
                while (prenos == 0)
                {
                    int pos = Helper.CoordinatesToPosition(array.DimensionSize, coords);
                    vals[v++] = new QChar(array.Data[pos]);

                    prenos = 1;
                    for (int j = 0; j < indexPositions.Length; j++)
                    {
                        indexPositions[j] += prenos;
                        prenos = indexPositions[j] / valuesForCoordinates[j].Length;
                        indexPositions[j] = indexPositions[j] % valuesForCoordinates[j].Length;
                        coords[j] = valuesForCoordinates[j][indexPositions[j]];
                    }
                }

                res.Add(new QCharNDimArray(vals, newDim));
                return res;
            }
            
            //sve isto kao i pre samo se radi o QBooleanNDimArray
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QBooleanNDimArray)
            {
                QBooleanNDimArray array = new QBooleanNDimArray(o.ElementAt(1));
                QBoolean[] vals = new QBoolean[valsSize];
                int v = 0;
                //kretanje kroz valuesForDimension. Prodje se kroz sve moguce vrednosti...
                int prenos = 0;
                while (prenos == 0)
                {
                    int pos = Helper.CoordinatesToPosition(array.DimensionSize, coords);
                    vals[v++] = new QBoolean(array.Data[pos]);

                    prenos = 1;
                    for (int j = 0; j < indexPositions.Length; j++)
                    {
                        indexPositions[j] += prenos;
                        prenos = indexPositions[j] / valuesForCoordinates[j].Length;
                        indexPositions[j] = indexPositions[j] % valuesForCoordinates[j].Length;
                        coords[j] = valuesForCoordinates[j][indexPositions[j]];
                    }
                }

                res.Add(new QBooleanNDimArray(vals, newDim));
                return res;
            }

            //sve isto samo se radi o QInt64NDimArray
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QInt64NDimArray)
            {
                QInt64NDimArray array = new QInt64NDimArray(o.ElementAt(1));
                QInt64[] vals = new QInt64[valsSize];
                int v = 0;
                //kretanje kroz valuesForDimension. Prodje se kroz sve moguce vrednosti...
                int prenos = 0;
                while (prenos == 0)
                {
                    int pos = Helper.CoordinatesToPosition(array.DimensionSize, coords);
                    vals[v++] = new QInt64(array.Data[pos]);

                    prenos = 1;
                    for (int j = 0; j < indexPositions.Length; j++)
                    {
                        indexPositions[j] += prenos;
                        prenos = indexPositions[j] / valuesForCoordinates[j].Length;
                        indexPositions[j] = indexPositions[j] % valuesForCoordinates[j].Length;
                        coords[j] = valuesForCoordinates[j][indexPositions[j]];
                    }
                }

                res.Add(new QInt64NDimArray(vals, newDim));
                return res;
            }
            
            //sve isto samo se radi o QUInt64NDimArray
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QUInt64NDimArray)
            {
                QUInt64NDimArray array = new QUInt64NDimArray(o.ElementAt(1));
                QUInt64[] vals = new QUInt64[valsSize];
                int v = 0;
                //kretanje kroz valuesForDimension. Prodje se kroz sve moguce vrednosti...
                int prenos = 0;
                while (prenos == 0)
                {
                    int pos = Helper.CoordinatesToPosition(array.DimensionSize, coords);
                    vals[v++] = new QUInt64(array.Data[pos]);

                    prenos = 1;
                    for (int j = 0; j < indexPositions.Length; j++)
                    {
                        indexPositions[j] += prenos;
                        prenos = indexPositions[j] / valuesForCoordinates[j].Length;
                        indexPositions[j] = indexPositions[j] % valuesForCoordinates[j].Length;
                        coords[j] = valuesForCoordinates[j][indexPositions[j]];
                    }
                }

                res.Add(new QUInt64NDimArray(vals, newDim));
                return res;
            }
            //sve isto samo se radi o QFloat64NDimArray...
            else if (o.ElementAt(1).GetEnumType() == IQLabValueType.QFloat64NDimArray)
            {
                QFloat64NDimArray array = arrayTemp;

                QFloat64[] vals = new QFloat64[valsSize];
                int v = 0;
                int prenos = 0;
                while (prenos == 0)
                {
                    int pos = Helper.CoordinatesToPosition(array.DimensionSize, coords);
                    vals[v++] = new QFloat64(array.Data[pos]);

                    prenos = 1;
                    for (int j = 0; j < indexPositions.Length; j++)
                    {
                        indexPositions[j] += prenos;
                        prenos = indexPositions[j] / valuesForCoordinates[j].Length;
                        indexPositions[j] = indexPositions[j] % valuesForCoordinates[j].Length;
                        coords[j] = valuesForCoordinates[j][indexPositions[j]];
                    }
                }

                res.Add(new QFloat64NDimArray(vals, newDim));
                return res;
            }
            else
                throw (new NumericalCalculationsException("??? Undefined function or method 'colon' for input arguments of type " +
                    Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
        }
    }
}
